package com.feri.sh.api.service.impl;

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.feri.sh.api.service.UserService;
import com.feri.sh.api.util.BaiduCensorUtil;
import com.feri.sh.api.util.JwtUtil;
import com.feri.sh.common.config.RedisKeyConfig;
import com.feri.sh.common.pojo.bo.UserAddBo;
import com.feri.sh.common.pojo.bo.UserFind;
import com.feri.sh.common.pojo.bo.UserLoginCode;
import com.feri.sh.common.pojo.bo.UserLoginPass;
import com.feri.sh.common.pojo.vo.R;
import com.feri.sh.core.dao.UserDao;
import com.feri.sh.core.dao.UserLogDao;
import com.feri.sh.core.entity.auth.User;
import com.feri.sh.core.entity.auth.UserLog;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * ━━━━━━Feri出没━━━━━━
 * 　　　┏┓　　　┏┓
 * 　　┏┛┻━━━┛┻┓
 * 　　┃　　　　　　 ┃
 * 　　┃　　　━　　　┃
 * 　　┃　┳┛　┗┳　  ┃
 * 　　┃　　　　　　 ┃
 * 　　┃　　　┻　　　┃
 * 　　┃　　　　　　　┃
 * 　　┗━┓　　　┏━┛
 * 　　　　┃　　　┃    邢哥的代码，怎么会，有bug呢，不可能！
 * 　　　　┃　　　┃
 * 　　　　┃　　　┗━━━┓
 * 　　　　┃　　　　　　　┣┓
 * 　　　　┃　　　　　　　┏┛
 * 　　　　┗┓┓┏━┳┓┏┛
 * 　　　　　┃┫┫　┃┫┫
 * 　　　　　┗┻┛　┗┻┛
 * ━━━━━━永无BUG!━━━━━━
 *
 * @Description:
 * @Author：邢朋辉
 * @Date: 2023/5/10 14:28
 */
@Service
public class UserServiceImpl implements UserService {
    @Resource
    private UserDao dao;
    @Resource
    private UserLogDao logDao;
    @Resource
    private StringRedisTemplate template;

    @Override
    public R checkPhone(String phone) {
        //1.验证
        if(StringUtils.hasLength(phone) && phone.matches("^1[3-9][0-9]{9}$"))
        {
            //2.查询数据库
            User user=dao.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone,phone));
            //3.校验 是否存在
            if(user==null){
                return R.ok();
            }else {
                return R.fail("亲,手机号已存在！");
            }
        }
        return R.fail("亲，请传递正确的手机号");
    }

    @Override
    public R checkName(String name) {
        //1.校验
        if(StringUtils.hasLength(name) && name.length()>=6){
            //校验 用户名不能纯数字
            //2.查询
            User user=dao.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername,name));
            //3.校验 是否存在
            if(user==null){
                if(BaiduCensorUtil.censorTxt(name)) {
                    return R.ok();
                }else {
                    //记录
                    return R.fail("亲，名称违规了！");
                }
            }else {
                return R.fail("亲,用户名已存在！");
            }
        }
        return R.fail("亲，请传递有效用户名");
    }

    @Override
    public R register(UserAddBo bo,String ip) {
        //1.校验
        if(Objects.nonNull(bo)){
//            if(bo.getUsername().matches())
            //2.校验密码
            if(bo.getPassword().matches("^(?=.*\\d)(?=.*[a-zA-Z])[a-zA-Z\\d]{6,18}$")){
                //3.校验手机号是否已经校验过验证码
                if(template.opsForSet().isMember(RedisKeyConfig.SMS_RCODE_CHECKED,bo.getPhone())) {//检查指定的手机号是否在Redis的Set中
                    //4.校验用户名 是否存在
                    User user=dao.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername,bo.getUsername()));
                    if(user==null){
                        //5.组装对象
                        user = new User();
                        BeanUtils.copyProperties(bo, user);
                        user.setCtime(new Date());
                        user.setFlag(1);
                        //6.密码 密文
                        user.setPassword(SecureUtil.sha256(bo.getPassword()));
                        //7.新增数据 到数据库
                        if (dao.insert(user) > 0) {
                            //8.记录日志
                            logDao.insert(new UserLog(user.getId(), "注册", "新增用户", ip));
                            //9.删除
                            template.opsForSet().remove(RedisKeyConfig.SMS_RCODE_CHECKED,bo.getPhone());

                            return R.ok();
                        }
                    }else {
                        return R.fail("亲，用户名已存在！请停止非法攻击");
                    }
                }else {
                    return R.fail("亲，请停止非法攻击，手机号根本就没有校验验证码！");
                }
            }else {
                return R.fail("亲，密码必须包含字母、数字、特殊符号组成！");
            }
        }
        return R.fail("亲，网络故障，注册失败！");
    }
    //策略模式

    /**
     * 根据账号密码登录
     */
    @Override
    public R loginPassword(UserLoginPass pass, String ip) {
        //1.校验
        if(pass!=null){
           // 2.查询
            LambdaQueryWrapper<User> wrapper=new LambdaQueryWrapper<User>();
            wrapper.eq(User::getUsername,pass.getAccount());
            wrapper.or().eq(User::getPhone,pass.getAccount());
            wrapper.eq(User::getFlag,1);
            User user=dao.selectOne(wrapper);

            //3.校验 用户是否存在
            if(Objects.nonNull(user)){
                //4.校验是否已在线
                if(!template.hasKey(RedisKeyConfig.AUTH_LOGIN_USER+user.getId())){
                    //5.校验密码
                    if(user.getPassword().equals(SecureUtil.sha256(pass.getPass()))){

                        //6.账号校验成功 生成令牌
                        user.setPassword("");
                        String json=JSON.toJSONString(user);
                        String token= JwtUtil.createJwt(json);
                        //7.存储到Redis 1.有效期 30分钟空闲时间 2.什么数据类型 3.存储什么数据 4.数据同步
                        template.opsForValue().set(RedisKeyConfig.AUTH_LOGIN_TOKEN+token,json,30, TimeUnit.MINUTES);
                        template.opsForValue().set(RedisKeyConfig.AUTH_LOGIN_USER+user.getId(),token,30, TimeUnit.MINUTES);

                        //8.记录日志
                        logDao.insert(new UserLog(user.getId(),"账号密码登录","通过账号密码登录成功！",ip));
                        return R.ok(token);
                    }
                }else {
                    //唯一登录 存在的2种方式：1.不允许登录，在线必须注销才可以 2.挤掉 推送挤掉信息
                    logDao.insert(new UserLog(user.getId(),"账号密码登录","失败，因为账号已在线！",ip));
                    return R.fail("亲，你的账号已在线，请勿重复登录！");
                }
            }
        }
        return R.fail("亲，账号或密码不正确！");
    }

    @Override
    public R loginCode(UserLoginCode code, String ip) {
        //1.校验参数
        if(code!=null) {
            //2.验证是否存在登录验证码
            if (template.hasKey(RedisKeyConfig.SMS_LCODE + code.getPhone())) {
                //3.查询
                LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>();
                wrapper.eq(User::getPhone, code.getPhone());
                wrapper.eq(User::getFlag, 1);
                User user = dao.selectOne(wrapper);
                //4.验证是否存在
                if (Objects.nonNull(user)) {
                    //5.校验是否已在线
                    if (!template.hasKey(RedisKeyConfig.AUTH_LOGIN_USER + user.getId())) {
                        //6.校验 验证码
                        if (template.opsForValue().get(RedisKeyConfig.SMS_LCODE + code.getPhone()).equals(code.getCode())) {

                            //7.账号校验成功 生成令牌
                            user.setPassword("");
                            String json = JSON.toJSONString(user);
                            String token = JwtUtil.createJwt(json);
                            //8.存储到Redis 1.有效期 30分钟空闲时间 2.什么数据类型 3.存储什么数据 4.数据同步
                            template.opsForValue().set(RedisKeyConfig.AUTH_LOGIN_TOKEN + token, json, 30, TimeUnit.MINUTES);
                            template.opsForValue().set(RedisKeyConfig.AUTH_LOGIN_USER + user.getId(), token, 30, TimeUnit.MINUTES);
                            //9. 删除登录验证码
                            template.delete(RedisKeyConfig.SMS_LCODE+code.getPhone());
                            //10.记录日志
                            logDao.insert(new UserLog(user.getId(), "手机号验证码登录", "通过手机号验证码登录成功！", ip));
                            return R.ok(token);
                        }
                    } else {
                        //唯一登录 存在的2种方式：1.不允许登录，在线必须注销才可以 2.挤掉 推送挤掉信息
                        logDao.insert(new UserLog(user.getId(), "手机号验证码登录", "失败，因为账号已在线！", ip));
                        return R.fail("亲，你的账号已在线，请勿重复登录！");
                    }
                }
            }
        }
        return R.fail("亲，手机号或验证码不正确！");

    }

    @Override
    public R checkToken(String token) {
        //1.验证参数
        if(StringUtils.hasLength(token)){
            //2.校验
            if(template.hasKey(RedisKeyConfig.AUTH_LOGIN_TOKEN+token)){
                return R.ok();
            }
        }
        return R.fail();
    }

    @Override
    public R all() {
        return R.ok(dao.selectList(null));
    }

    @Override
    public R logout(String token, String ip) {
        //1.验证令牌是否存在
//        if(template.hasKey(RedisKeyConfig.AUTH_LOGIN_TOKEN+token)){
            //2.令牌有效  销毁
            int uid=JSON.parseObject(template.opsForValue().get(RedisKeyConfig.AUTH_LOGIN_TOKEN+token),User.class).getId();
            template.delete(RedisKeyConfig.AUTH_LOGIN_USER+uid);
            template.delete(RedisKeyConfig.AUTH_LOGIN_TOKEN+token);
            //3.记录日志
            logDao.insert(new UserLog(uid, "密码找回", "通过密码找回设置了新密码", ip));
            return R.ok();
//        }
//        return R.fail("亲，你来告诉我，你都没登录，怎么退出！");
    }

    @Override
    public R updatePass(String pass, String token, String ip) {
        //1.验证参数
        if(StringUtils.hasLength(pass) && pass.matches("^(?=.*\\d)(?=.*[a-zA-Z])[a-zA-Z\\d]{6,18}$")) {
            //2.通过令牌获取uid
//            if (template.hasKey(RedisKeyConfig.AUTH_LOGIN_TOKEN + token)) {
                int uid = JSON.parseObject(template.opsForValue().get(RedisKeyConfig.AUTH_LOGIN_TOKEN + token), User.class).getId();
                //3.密码 密文
                User user = new User();
                user.setPassword(SecureUtil.sha256(pass));
                user.setId(uid);
                //4.操作数据库
                if (dao.updateById(user) > 0) {
                    //5.修改成功 强制下线
                    template.delete(RedisKeyConfig.AUTH_LOGIN_USER + uid);
                    template.delete(RedisKeyConfig.AUTH_LOGIN_TOKEN + token);
                    //6.记录日志
                    logDao.insert(new UserLog(user.getId(), "修改密码", "通过密码修改设置了新的密码", ip));
                    return R.ok();
                }
//            }
        }
        return R.fail("修改密码失败！");
    }
    /**
     * 查询操作日志*/
    @Override
    public R queryMyLog(String token) {
        int uid=JSON.parseObject(template.opsForValue().get(RedisKeyConfig.AUTH_LOGIN_TOKEN+token),User.class).getId();
        return R.ok(logDao.selectList(new LambdaQueryWrapper<UserLog>().eq(UserLog::getUid,uid).orderByDesc(UserLog::getCtime)));
    }

    @Override
    public R queryByToken(String token) {
        return R.ok(JSON.parseObject(template.opsForValue().get(RedisKeyConfig.AUTH_LOGIN_TOKEN+token),User.class));
    }

    @Override
    public R findPass(UserFind find,String ip) {
        //1.验证参数
        if(find!=null){
            if(StringUtils.hasLength(find.getPass()) && find.getPass().matches("^(?=.*\\d)(?=.*[a-zA-Z])[a-zA-Z\\d]{6,18}$")) {
                //2.验证是否存在验证码
                if (template.hasKey(RedisKeyConfig.SMS_FCODE + find.getPhone())) {
                    //3.校验 验证码是否正确
                    if (template.opsForValue().get(RedisKeyConfig.SMS_FCODE + find.getPhone()).equals(find.getCode())) {
                        //验证码正确 密码转为密文
                        String pass = SecureUtil.sha256(find.getPass());
                        //5.操作数据库
                        User user = new User();
                        user.setPhone(find.getPhone());
                        user.setPassword(pass);
                        if (dao.update(user, new LambdaUpdateWrapper<User>().eq(User::getPhone, user.getPhone())) > 0) {
                            //6.如果此时 账号在线。需要强制下线
                            int uid = (dao.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, find.getPhone()))).getId();
                            //验证是否在线
                            if (template.hasKey(RedisKeyConfig.AUTH_LOGIN_USER + uid)) {
                                //在线 需要下线 删除
                                String token = template.opsForValue().get(RedisKeyConfig.AUTH_LOGIN_USER + uid);
                                template.delete(RedisKeyConfig.AUTH_LOGIN_TOKEN + token);
                                template.delete(RedisKeyConfig.AUTH_LOGIN_USER + uid);
                            }
                            //7.记录操作日志
                            logDao.insert(new UserLog(uid, "密码找回", "通过密码找回设置了新密码", ip));
                            return R.ok();
                        }
                    }
                }
            }
        }
        return R.fail("亲,密码找回失败！请联系客服");
    }


}
